﻿using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using RevitProject.Helper;
using RevitProject.SpaceManager.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using Newtonsoft.Json;
using RevitProject.SpaceManager.Configuration;
using System.Windows;
using System.Collections;
using Autodesk.Revit.ApplicationServices;
using Application = Autodesk.Revit.ApplicationServices.Application;
using RevitProject.REnvironment;
using RevitProject.SpaceManager.ViewModel.SpaceViewModel;
using RevitProject.SpaceManager.ViewModel.HAVCLoadViewModelPart;
using RevitProject.SpaceManager.ViewModel.RoomViewModelPart;
using RevitProject.SpaceManager.ViewModel.NavRoomVmPart;
using RevitProject.SpaceManager.Helper;
using RevitProject.SpaceManager.Core;
namespace RevitProject.SpaceManager.ViewModel
{
    /// <summary>
    /// 用于传递给归类命令的参数对象。
    /// </summary>
    public class AssignCategoryParameter
    {
        public CategoryTreeNodeViewModel TargetCategoryNode { get; set; }
        // *** 修改：传递 RoomViewModel 列表，而不是非泛型 IList ***
        public List<RoomViewModel> SelectedRoomVMs { get; set; }
        // ******************************************************
    }

    // 定义导航视图枚举
    public enum NavView
    {
        Rooms,          // 房间视图 (默认)
        Spaces,
        Areas,
        AnalysisSpace,
        AnalysisSurface,
        LinkData,
        HAVCLoadType,
    }

    /// <summary>
    /// 空间管理器的主视图模型 (ViewModel)。手动实现所有属性。
    /// </summary>
    public partial class SpaceManagerVm : ObservableObject // 保持 partial
    {
        // --- 私有字段 ---
        private readonly Document _doc;
        private readonly UIDocument _uiDoc;
        private readonly UIApplication _uiApp;
        private readonly Application _app;
        private readonly SpaceManagerView m_MainView;
        private List<Room> _allRooms = new List<Room>();
        private readonly string _configFilePath;
        public static readonly object AllLevelsPlaceholder = new object();
        private NavRoomService _navRoomService;
        public NavRoomViewModel NavRoomVM { get; private set; }
        // --- 手动实现的属性 ---
        private List<object> _levels;
        public List<object> Levels { get => _levels; private set => SetProperty(ref _levels, value); }

        private object _selectedLevel; // 可以是 Level 对象或 AllLevelsPlaceholder
        public object SelectedLevel
        {
            get => _selectedLevel;
            set
            {
                if (SetProperty(ref _selectedLevel, value))
                {
                    Debug.WriteLine($"SelectedLevel changed to: {GetSelectedLevelName()}"); // 使用辅助方法获取名称
                    ApplyFiltersAndUpdateView();
                }
            }
        }

        // 新增：当前视图属性
        private NavView _currentView = NavView.Rooms; // 默认选中房间视图
        public NavView CurrentView
        {
            get => _currentView;
            private set => SetProperty(ref _currentView, value); // 使用 SetProperty 来触发通知
        }

        // 新增：空间页签的 ViewModel 实例
        public SpaceTabViewModel SpaceVM { get; private set; }

        public HAVCLoadViewModel HAVCLoadVM { get; private set; }
        // 新增：当前活动的内容 ViewModel (用于绑定到 View 的内容区域)
        private object _activeContentViewModel;
        public object ActiveContentViewModel
        {
            get => _activeContentViewModel;
            private set => SetProperty(ref _activeContentViewModel, value);
        }


        private ObservableCollection<CategoryTreeNodeViewModel> _categoryTreeNodes = new ObservableCollection<CategoryTreeNodeViewModel>();
        public ObservableCollection<CategoryTreeNodeViewModel> CategoryTreeNodes { get => _categoryTreeNodes; private set => SetProperty(ref _categoryTreeNodes, value); }

        private CategoryTreeNodeViewModel _selectedCategoryNode;
        public CategoryTreeNodeViewModel SelectedCategoryNode { get => _selectedCategoryNode; set { if (SetProperty(ref _selectedCategoryNode, value)) UpdateRoomList(); } }

        private ObservableCollection<RoomViewModel> _filteredRoomList = new ObservableCollection<RoomViewModel>();
        public ObservableCollection<RoomViewModel> FilteredRoomList
        {
            get => _filteredRoomList;
            private set
            {
                // *** 在 SetProperty 之前记录旧 Count ***
                int oldCount = _filteredRoomList?.Count ?? 0;
                if (SetProperty(ref _filteredRoomList, value)) // SetProperty 返回 true 表示值已改变
                {
                    int newCount = _filteredRoomList?.Count ?? 0;
                    Debug.WriteLine($"FilteredRoomList 属性已更新。旧数量: {oldCount}, 新数量: {newCount}");
                    // *** 显式触发 FilteredRoomCount 的变更通知 ***
                    OnPropertyChanged(nameof(FilteredRoomCount));
                    Debug.WriteLine($"已为 FilteredRoomCount 发出 OnPropertyChanged 通知。");
                }
                else
                {
                    Debug.WriteLine($"FilteredRoomList 未更新（新旧值相同）。");
                    // *** 即使列表实例没变，如果内容可能变了也通知一下 Count (可选，但安全) ***
                    // OnPropertyChanged(nameof(FilteredRoomCount));
                }
            }
        }
        // *** 用于绑定的 Count 属性 ***
        public int FilteredRoomCount => FilteredRoomList?.Count ?? 0;

        private string _roomFilterText = string.Empty;
        /// <summary>
        /// 用于筛选房间列表的文本 (按名称或编号)。
        /// </summary>
        public string RoomFilterText
        {
            get => _roomFilterText;
            set
            {
                // 使用 SetProperty 更新字段并触发通知
                if (SetProperty(ref _roomFilterText, value))
                {
                    // 当筛选文本改变时，重新执行列表更新逻辑以应用筛选
                    Debug.WriteLine($"筛选文本改变为: '{value}'");
                    UpdateRoomList(); // 调用更新列表的方法
                }
            }
        }

        // --- 命令 ---
        public IAsyncRelayCommand LoadDataCommand { get; }
        public IRelayCommand<object> UpdateSelectedNodeCommand { get; }
        public IRelayCommand<CategoryTreeNodeViewModel> AddChildCategoryCommand { get; }
        public IRelayCommand<CategoryTreeNodeViewModel> RenameCategoryCommand { get; }
        public IRelayCommand<CategoryTreeNodeViewModel> DeleteCategoryCommand { get; }

        // --- 新增：处理拖放归类的命令 ---
        public IRelayCommand<AssignCategoryParameter> DropRoomsOntoCategoryCommand { get; }
        // *********************************
        public IRelayCommand EnsureSharedParameterCommand { get; }
        // 新增：导航命令
        public IRelayCommand<NavView> NavigateCommand { get; }
        // --- 构造函数 ---
        public SpaceManagerVm(SpaceManagerView view, Document doc, UIApplication uiApp)
        {
            _doc = doc ?? throw new ArgumentNullException(nameof(doc));
            _uiApp = uiApp ?? throw new ArgumentNullException(nameof(uiApp));
            _app = uiApp.Application; // 获取 Revit Application
            _uiDoc = new UIDocument(doc); // 创建 UIDocument
            m_MainView = view;

            SpaceVM = new SpaceTabViewModel(doc, uiApp);
            HAVCLoadVM = new HAVCLoadViewModel(view, doc, uiApp, this);
            Debug.WriteLine($"SpaceManagerVm Constructor: HAVCLoadVM initialized: {HAVCLoadVM != null}"); // <-- 添加检查

            try
            {
                string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
                string configDir = Path.Combine(appDataPath, "维树科技", "空间管理器"); // 请替换为实际名称
                Directory.CreateDirectory(configDir);
                _configFilePath = Path.Combine(configDir, "SpaceCategories.json");
                Debug.WriteLine($"配置文件路径设置为: {_configFilePath}");
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"错误：无法设置配置文件路径 - {ex.Message}");
                _configFilePath = "SpaceCategories.json"; // 降级
                TaskDialog.Show("警告", $"无法访问用户配置目录，分类配置可能无法保存。\n错误: {ex.Message}");
            }

            _navRoomService = new NavRoomService(_uiApp);
            NavRoomVM = new NavRoomViewModel(_uiApp, _navRoomService);

            LoadDataCommand = new AsyncRelayCommand(LoadShellDataAsync); // 指向新的加载逻辑
            NavigateCommand = new RelayCommand<NavView>(ExecuteNavigate); // 导航命令
            // 初始化命令
            LoadDataCommand = new AsyncRelayCommand(LoadInitialDataAsync);
            UpdateSelectedNodeCommand = new RelayCommand<object>(selectedItem => { SelectedCategoryNode = selectedItem as CategoryTreeNodeViewModel; });
            AddChildCategoryCommand = new RelayCommand<CategoryTreeNodeViewModel>(ExecuteAddChildCategory);
            RenameCategoryCommand = new RelayCommand<CategoryTreeNodeViewModel>(ExecuteRenameCategory, CanExecuteModifyCategory);
            DeleteCategoryCommand = new RelayCommand<CategoryTreeNodeViewModel>(ExecuteDeleteCategory, CanExecuteModifyCategory);
            NavigateCommand = new RelayCommand<NavView>(ExecuteNavigate);
            // *** 初始化拖放命令 ***
            DropRoomsOntoCategoryCommand = new RelayCommand<AssignCategoryParameter>(ExecuteAssignRoomsToSpecificCategory, CanExecuteAssignRoomsToSpecificCategory); // 复用之前的执行和判断逻辑
            // *********************
            EnsureSharedParameterCommand = new RelayCommand(ExecuteEnsureSharedParameter);
            ActiveContentViewModel = NavRoomVM;
            CurrentView = NavView.Rooms;
        }

        // --- 命令执行方法 ---

        /// <summary>
        /// 加载主外壳所需的数据并触发默认视图的数据加载。
        /// *不再加载旧房间视图数据*
        /// </summary>
        private async Task LoadShellDataAsync()
        {
            Debug.WriteLine("SpaceManagerVm: 加载外壳数据并初始化默认视图 (最小化改动版)...");
            // 可以保留加载 Levels 列表，如果其他视图也需要它
            // await Task.Run(() => { /* 加载共享数据，如 Levels */ });

            // 触发默认视图 (NavRoomVM) 的数据加载
            if (NavRoomVM != null && NavRoomVM.LoadDataCommand.CanExecute(null))
            {
                try
                {
                    await NavRoomVM.LoadDataCommand.ExecuteAsync(null);
                }
                catch (Exception ex) { /* ... 错误处理 ... */ }
            }
            Debug.WriteLine("SpaceManagerVm: 外壳加载完成。");
        }



        // 导航命令执行方法
        private async void ExecuteNavigate(NavView targetView) // 改为 async void 以便调用异步加载
        {
            if (CurrentView == targetView && ActiveContentViewModel != null) // 添加 ActiveContentViewModel 检查，确保初始导航时能正确设置
            {
                Debug.WriteLine($"导航请求被忽略，因为目标视图 ({targetView}) 与当前视图相同。");
                return;
            }

            CurrentView = targetView;
            Debug.WriteLine($"导航到: {targetView}");
            object targetViewModel = null; // 先确定目标 ViewModel
            Func<Task> loadAction = null; // 加载数据的动作
            bool dataLoaded = false;

            switch (targetView)
            {
                case NavView.Rooms:
                    ActiveContentViewModel = NavRoomVM; // *** 关键修改：指向新的 ViewModel ***
                    Debug.WriteLine("激活 NavRoomViewModel (新房间视图)。");
                    if (NavRoomVM != null && NavRoomVM.LoadDataCommand.CanExecute(null))
                    {
                        
                        Debug.WriteLine("请求加载新房间数据...");
                        await NavRoomVM.LoadDataCommand.ExecuteAsync(null);
                        dataLoaded = true;
                    }

                    break;
                case NavView.Spaces:
                    ActiveContentViewModel = SpaceVM; // 空间视图的数据上下文是 SpaceTabViewModel
                    Debug.WriteLine("激活 SpaceTabViewModel (空间视图)。");
                    // 触发空间数据的加载 (如果尚未加载)
                    if (SpaceVM.LoadSpaceDataCommand.CanExecute(null))
                    {
                        Debug.WriteLine("请求加载空间数据...");
                        await SpaceVM.LoadSpaceDataCommand.ExecuteAsync(null);
                        dataLoaded = true;
                    }
                    else
                    {
                        Debug.WriteLine("空间数据加载命令当前不可执行 (可能正在加载或已加载)。");
                        // 检查列表是否已有数据，如果列表不为空，也认为数据已“加载”
                        if (SpaceVM.FilteredSpaceList != null && SpaceVM.FilteredSpaceList.Any())
                        {
                            dataLoaded = true;
                        }
                    }
                    break;

                case NavView.HAVCLoadType: // 处理空间类型视图
                    ActiveContentViewModel = HAVCLoadVM; // 设置活动 VM
                    Debug.WriteLine("激活 HAVCLoadViewModel (空间类型视图)。");
                    // 触发空间类型数据的加载 (如果尚未加载)
                    if (HAVCLoadVM.LoadDataCommand.CanExecute(null))
                    {
                        Debug.WriteLine("请求加载空间类型数据...");
                        await HAVCLoadVM.LoadDataCommand.ExecuteAsync(null);
                        dataLoaded = true;
                    }
                    else
                    {
                        Debug.WriteLine("空间类型数据加载命令当前不可执行 (可能正在加载或已加载)。");
                        // 检查列表是否已有数据
                        if (HAVCLoadVM.SpaceTypeList != null && HAVCLoadVM.SpaceTypeList.Any())
                        {
                            dataLoaded = true;
                        }
                    }
                    break;

                // ... 处理其他视图 (Areas, AnalysisSpace, AnalysisSurface, LinkData) ...
                case NavView.Areas:
                case NavView.AnalysisSpace:
                case NavView.AnalysisSurface:
                case NavView.LinkData:
                default:
                    ActiveContentViewModel = null; // 其他视图暂时显示空白
                    Debug.WriteLine($"导航到未实现的视图: {targetView}。ActiveContentViewModel 设置为 null。");
                    break;
            }

            if (ActiveContentViewModel != null && !dataLoaded)
            {
                Debug.WriteLine($"视图 {targetView} 切换完成，但数据似乎未加载。");
            }

        }
        /// <summary>
        /// 在 Revit API 上下文中，更新指定房间列表的“空间类别”参数值。
        /// </summary>
        /// <param name="doc">当前的 Revit 文档。</param>
        /// <param name="app">当前的 Revit 应用实例。</param>
        /// <param name="roomIdsToUpdate">需要更新的房间的 ElementId 列表。</param>
        /// <param name="newCategoryValue">要设置的新的空间类别名称。</param>
        /// <param name="transactionName">用于 Revit 事务的名称。</param>
        /// <returns>如果事务成功提交（至少更新了一个房间）则返回 true，否则返回 false。</returns>
        private bool UpdateRoomCategoryParameterInRevit(Document doc, Application app, IEnumerable<ElementId> roomIdsToUpdate, string newCategoryValue, string transactionName)
        {
            // 输入参数校验
            if (doc == null || app == null || roomIdsToUpdate == null || !roomIdsToUpdate.Any() || string.IsNullOrEmpty(newCategoryValue))
            {
                Debug.WriteLine("UpdateRoomCategoryParameterInRevit: 无效的输入参数。");
                return false;
            }

            // 1. 获取共享参数定义
            ExternalDefinition sharedParamDef = SharedParameterHelper.EnsureParameterExistsAndBound(app, doc);
            if (sharedParamDef == null)
            {
                Debug.WriteLine("UpdateRoomCategoryParameterInRevit: 无法确保共享参数存在或已绑定。");
                return false;
            }

            // 2. 执行事务来修改参数
            using (Transaction tx = new Transaction(doc, transactionName))
            {
                try
                {
                    tx.Start(); // 开始事务
                    int successCount = 0;
                    int failureCount = 0;

                    // 遍历需要更新的房间 ID
                    foreach (ElementId roomId in roomIdsToUpdate)
                    {
                        Room room = doc.GetElement(roomId) as Room;
                        if (room == null || !room.IsValidObject)
                        {
                            Debug.WriteLine($"UpdateRoomCategoryParameterInRevit: 跳过无效的房间 ID {roomId}。");
                            failureCount++;
                            continue;
                        }

                        // 获取参数实例
                        Parameter param = room.get_Parameter(sharedParamDef);

                        // 检查参数是否有效且可写
                        if (param != null && !param.IsReadOnly)
                        {
                            // 尝试设置新值
                            if (param.Set(newCategoryValue))
                            {
                                successCount++; // 成功计数
                            }
                            else
                            {
                                Debug.WriteLine($"UpdateRoomCategoryParameterInRevit: 未能设置房间 {room.Id} 的参数值为 '{newCategoryValue}'。");
                                failureCount++; // 失败计数
                            }
                        }
                        else
                        {
                            Debug.WriteLine($"UpdateRoomCategoryParameterInRevit: 房间 {room.Id} 上未找到参数 '{SharedParameterHelper.SharedParameterName}' 或参数只读。");
                            failureCount++; // 视为失败
                        }
                    } // foreach 结束

                    // 根据结果提交或回滚
                    if (successCount > 0)
                    {
                        tx.Commit(); // 提交更改
                        Debug.WriteLine($"UpdateRoomCategoryParameterInRevit: 事务 '{transactionName}' 已提交。成功更新 {successCount} 个房间。失败 {failureCount} 个。");
                        return true; // 返回成功
                    }
                    else
                    {
                        tx.RollBack(); // 回滚更改
                        Debug.WriteLine($"UpdateRoomCategoryParameterInRevit: 事务 '{transactionName}' 已回滚。没有成功更新的房间。失败 {failureCount} 个。");
                        return false; // 返回失败
                    }
                }
                catch (Exception ex) // 捕获事务中的异常
                {
                    Debug.WriteLine($"UpdateRoomCategoryParameterInRevit: 事务 '{transactionName}' 中发生异常: {ex.ToString()}");
                    if (tx.HasStarted() && tx.GetStatus() == TransactionStatus.Started)
                    {
                        tx.RollBack(); // 回滚
                    }
                    TaskDialog.Show("错误", $"更新房间参数时出错: {ex.Message}");
                    return false; // 返回失败
                }
            } // using Transaction 结束
        }

        /// <summary>
        /// 异步加载初始数据，并在加载后设置默认楼层为“全部”。
        /// </summary>
        private async Task LoadInitialDataAsync()
        {
            Debug.WriteLine("开始加载数据...");
            List<Level> actualLevels = null; List<Room> allRooms = null;
            try
            {
                await Task.Run(() => { try { actualLevels = new FilteredElementCollector(_doc).OfClass(typeof(Level)).Cast<Level>().OrderBy(o => o.Elevation).ToList(); } catch { actualLevels = new List<Level>(); } try { allRooms = GetAllRooms(_doc); } catch { allRooms = new List<Room>(); } });
                var levelsForComboBox = new List<object>();
                levelsForComboBox.Add(AllLevelsPlaceholder); // 添加占位符
                levelsForComboBox.AddRange(actualLevels ?? new List<Level>());
                Levels = levelsForComboBox; // 更新绑定源
                _allRooms = allRooms ?? new List<Room>();


                SelectedLevel = AllLevelsPlaceholder; // 设置默认选中项为占位符

                SelectedCategoryNode = CategoryTreeNodes.FirstOrDefault();
                /*RevitContextHelper.Execute(app => TaskDialog.Show("加载完成", "楼层、房间和分类配置已加载。"));*/ 
                Debug.WriteLine("数据加载完成。");
            }
            catch (Exception ex) { /* 错误处理 */ Debug.WriteLine($"数据加载过程中发生严重错误: {ex.Message}"); RevitContextHelper.Execute(app => TaskDialog.Show("错误", $"加载数据时出错: {ex.Message}")); }
        }

        /// <summary>
        /// 执行添加子分类的操作。
        /// </summary>
        private void ExecuteAddChildCategory(CategoryTreeNodeViewModel parentNode)
        {
            string promptTitle = parentNode == null ? "添加根分类" : "添加子分类";
            string promptMessage = parentNode == null ? "请输入新的根分类名称:" : $"在“{parentNode.DisplayName}”下添加子分类，请输入名称:";
            string defaultName = parentNode == null ? "新根分类" : "新子分类";
            var inputDialog = new InputDialog(promptMessage, promptTitle, defaultName) { Owner = m_MainView };
            if (inputDialog.ShowDialog() == true)
            {
                string newCategoryName = inputDialog.ResponseText;
                if (!string.IsNullOrWhiteSpace(newCategoryName))
                {
                    bool nameExists = parentNode == null
                        ? CategoryTreeNodes.Any(n => n.DisplayName.Equals(newCategoryName, StringComparison.OrdinalIgnoreCase))
                        : parentNode.Children.Any(child => child.DisplayName.Equals(newCategoryName, StringComparison.OrdinalIgnoreCase));
                    if (nameExists)
                    { RevitContextHelper.Execute(app => TaskDialog.Show("错误", $"名称“{newCategoryName}”已存在。")); }
                    else
                    {
                        var newNode = new CategoryTreeNodeViewModel(newCategoryName, parent: parentNode);
                        if (parentNode == null) { CategoryTreeNodes.Add(newNode); /* TODO: 排序 */ }
                        else { parentNode.Children.Add(newNode); /* TODO: 排序 */ parentNode.IsExpanded = true; }
                        Debug.WriteLine($"添加了分类 '{newCategoryName}'。");
                        SaveCategoryConfiguration(); // 保存更改
                    }
                }
                else { RevitContextHelper.Execute(app => TaskDialog.Show("提示", "输入的分类名称不能为空。")); }
            }
            else { Debug.WriteLine("添加分类操作已取消。"); }
        }


        /// <summary>
        /// 执行重命名分类的操作。
        /// 更新界面节点，保存配置，并直接更新【原先】与该节点关联的房间的 Revit 参数，然后执行完整刷新。
        /// </summary>
        /// <param name="nodeToRename">要重命名的 ViewModel 节点。</param>
        private void ExecuteRenameCategory(CategoryTreeNodeViewModel nodeToRename)
        {
            // 检查传入的节点是否有效
            if (nodeToRename == null) return;

            // 防止重命名特殊的“全部房间”或“其他类”节点
            if (nodeToRename.IsAllRoomsNode || nodeToRename.DisplayName.Equals("其他类", StringComparison.OrdinalIgnoreCase))
            {
                RevitContextHelper.Execute(
                    app => TaskDialog.Show("提示", "不能重命名“全部房间”或“其他类”节点。")
                    );
                return;
            }

            //   步骤 1: 捕获当前与节点关联的房间 ID 和旧名称    
            string oldName = nodeToRename.DisplayName; // 保存旧名称
            // 获取当前 ViewModel 节点直接关联的房间 ID 列表 (创建副本)
            List<ElementId> roomIdsToUpdate = nodeToRename.AssociatedRooms?
                .Select(r => r.Id)
                .Where(id => id != ElementId.InvalidElementId)
                .ToList() ?? new List<ElementId>();

            Debug.WriteLine($"节点 '{oldName}' 在重命名前关联了 {roomIdsToUpdate.Count} 个房间，将尝试更新这些房间。");

            // 弹出输入对话框，让用户输入新名称
            var inputDialog = new InputDialog($"请输入“{oldName}”的新名称:", "重命名分类", oldName) { Owner = m_MainView };

            // 如果用户点击了“确定”
            if (inputDialog.ShowDialog() == true)
            {
                // 获取用户输入的新名称，并去除首尾空格
                string newName = inputDialog.ResponseText?.Trim();

                // 验证新名称是否有效（非空、非空白，且与旧名称不同）
                if (!string.IsNullOrWhiteSpace(newName) && newName != oldName)
                {
                    // 检查新名称在同级节点中是否已存在
                    var parentNode = nodeToRename.Parent; // 获取父节点 (应该是 allRoomsNode)
                    var siblings = parentNode?.Children ?? new ObservableCollection<CategoryTreeNodeViewModel>(); // 获取同级节点列表
                    bool nameExists = siblings.Any(s => s != nodeToRename && s.DisplayName.Equals(newName, StringComparison.OrdinalIgnoreCase));

                    // 如果名称已存在，提示用户并返回
                    if (nameExists)
                    {
                        RevitContextHelper.Execute(app => TaskDialog.Show("错误", $"同级分类下已存在名称“{newName}”。"));
                    }
                    else // 名称有效且不重复
                    {
                        Debug.WriteLine($"准备将分类 '{oldName}' 重命名为 '{newName}'。");

                        // --- 步骤 2: 更新 ViewModel 节点的显示名称 ---
                        nodeToRename.DisplayName = newName;
                        Debug.WriteLine($"ViewModel DisplayName temporarily updated to '{newName}'.");

                        // --- 步骤 3: 保存更新后的分类结构到 JSON 配置文件 ---
                        SaveCategoryConfiguration();
                        Debug.WriteLine("Configuration saved with new name.");

                        // --- 步骤 4: 在 Revit API 上下文中更新【之前捕获的】房间的参数值 ---
                        bool revitUpdateSuccess = false; // 标记 Revit 操作是否成功
                        if (roomIdsToUpdate.Any())
                        {
                            RevitContextHelper.Execute(uiApp =>
                            {
                                // 调用封装好的辅助方法来执行更新
                                revitUpdateSuccess = UpdateRoomCategoryParameterInRevit(
                                    uiApp.ActiveUIDocument.Document,
                                    uiApp.Application,
                                    roomIdsToUpdate, // 使用步骤 1 中捕获的 ID 列表  
                                    newName,         // 使用用户输入的新名称  
                                    $"重命名类别参数从 {oldName} 到 {newName}" // 事务名称
                                );
                            });
                            Debug.WriteLine($"--- DEBUG: Returned from RevitContextHelper.Execute. Revit update success flag = {revitUpdateSuccess} ---");
                        }
                        else
                        {
                            Debug.WriteLine("由于节点最初没有关联房间，跳过 Revit 参数更新步骤。");
                            revitUpdateSuccess = true; // 没有房间需要更新也视为 Revit 操作“成功”完成
                        }

                        // --- 步骤 5: 【直接调用】完整刷新逻辑 ---
                        if (revitUpdateSuccess) // 仅在 Revit 更新成功或无需更新时刷新
                        {
                            try
                            {
                                Debug.WriteLine($"请求在重命名后执行【完整刷新】 ApplyFiltersAndUpdateView (Direct Call)...");
                                ApplyFiltersAndUpdateView(); // 直接调用完整刷新  
                                Debug.WriteLine("ApplyFiltersAndUpdateView 调用完成 (Direct Call)。");

                                // --- 步骤 6: 尝试重新选中节点 (直接执行) ---
                                Debug.WriteLine($"尝试在刷新后重新选中节点 '{newName}' (Direct Call)...");
                                var newNodeInstance = FindNodeByName(CategoryTreeNodes, newName);
                                bool nodeFoundAndSet = false;
                                if (newNodeInstance != null)
                                {
                                    ExpandParents(newNodeInstance);
                                    newNodeInstance.IsSelected = true;
                                    SelectedCategoryNode = newNodeInstance; // 设置选中项
                                    nodeFoundAndSet = SelectedCategoryNode == newNodeInstance;
                                    Debug.WriteLine($"已尝试直接设置 SelectedCategoryNode 为 '{newNodeInstance.DisplayName}'。");
                                }
                                else
                                {
                                    Debug.WriteLine($"未能重新选中节点 '{newName}'。将选中根节点。");
                                    SelectedCategoryNode = CategoryTreeNodes.FirstOrDefault(); // 回退选择
                                    nodeFoundAndSet = SelectedCategoryNode != null;
                                }

                                // --- 步骤 7: 强制更新列表 (直接执行) ---
                                if (nodeFoundAndSet)
                                {
                                    Debug.WriteLine($"强制直接调用 UpdateRoomList 为节点: '{SelectedCategoryNode?.DisplayName ?? "NULL"}'");
                                    UpdateRoomList(); // 直接调用列表更新  
                                }
                                else
                                {
                                    Debug.WriteLine("未能设置有效的 SelectedCategoryNode，跳过强制 UpdateRoomList 调用。");
                                }
                                Debug.WriteLine("--- DEBUG: Direct refresh calls completed ---");
                            }
                            catch (Exception directRefreshEx) // 捕获直接刷新过程中可能发生的异常
                            {
                                Debug.WriteLine($"!!!!! EXCEPTION during direct refresh calls: {directRefreshEx.ToString()}");
                                TaskDialog.Show("刷新错误", $"界面刷新时遇到错误: {directRefreshEx.Message}");
                            }
                        }
                        else // 如果 Revit 更新失败，则不执行刷新
                        {
                            Debug.WriteLine("由于 Revit 参数更新未成功，跳过界面刷新。");
                            // 也许给用户一个提示更好？
                            // TaskDialog.Show("提示", "未能成功更新所有相关房间的参数值，界面将不会完全刷新以反映更改。");
                        }

                    } // else (名称检查通过) 结束
                } // if (新名称有效且不同) 结束
                // 处理新名称无效或与旧名称相同的情况
                else if (string.IsNullOrWhiteSpace(newName))
                {
                    RevitContextHelper.Execute(app => TaskDialog.Show("提示", "输入的分类名称不能为空。"));
                }
                // else: 新旧名称相同，无需操作
            } // if (对话框点击了确定) 结束
            else // 用户点击了取消
            {
                Debug.WriteLine("重命名操作已取消。");
            }
        } // ExecuteRenameCategory 方法结束
        /// <summary>
        /// 执行删除分类的操作。
        /// </summary>
        // In SpaceManagerVm.cs
        private void ExecuteDeleteCategory(CategoryTreeNodeViewModel nodeToDelete)
        {
            if (nodeToDelete == null) return;
            const string defaultCategoryNodeName = "其他类";
            const string allRoomsNodeName = "全部房间"; // Define constant for clarity

            CategoryTreeNodeViewModel otherCategoryNode = null; // Initialize as null

            // ---   开始修改查找逻辑   ---
            // 1. 检查 CategoryTreeNodes 是否符合预期的结构 (只有一个根节点，且是 "全部房间")
            if (CategoryTreeNodes != null && CategoryTreeNodes.Count == 1 && CategoryTreeNodes[0].IsAllRoomsNode)
            {
                // 2. 获取 "全部房间" 节点
                var rootNode = CategoryTreeNodes[0];

                // 3. 在 "全部房间" 节点的子节点中直接查找 "其他类"
                if (rootNode.Children != null)
                {
                    otherCategoryNode = rootNode.Children.FirstOrDefault(
                        child => child.DisplayName.Equals(defaultCategoryNodeName, StringComparison.OrdinalIgnoreCase)
                    );
                    Debug.WriteLine($"ExecuteDeleteCategory: Searching for '{defaultCategoryNodeName}' in children of '{rootNode.DisplayName}'. Found: {otherCategoryNode != null}");
                }
                else
                {
                    Debug.WriteLine($"ExecuteDeleteCategory WARNING: Root node '{rootNode.DisplayName}' has no children to search within.");
                }
            }
            else
            {
                // 如果 CategoryTreeNodes 结构不符合预期，记录错误
                Debug.WriteLine($"ExecuteDeleteCategory ERROR: CategoryTreeNodes structure is unexpected. Count={CategoryTreeNodes?.Count ?? -1}. Expected single root node named '{allRoomsNodeName}' with IsAllRoomsNode=true.");
                // 你也可以尝试用旧的 FindNodeByName 作为备用查找，但这可能隐藏问题
                // otherCategoryNode = FindNodeByName(CategoryTreeNodes, defaultCategoryNodeName);
                // Debug.WriteLine($"ExecuteDeleteCategory: Fallback search using FindNodeByName. Found: {otherCategoryNode != null}");
            }
            // ---   查找逻辑修改结束   ---


            // 4. 检查是否找到了 "其他类" 节点
            if (otherCategoryNode == null)
            {
                Debug.WriteLine($"ExecuteDeleteCategory ERROR: Failed to find the target node '{defaultCategoryNodeName}' for reclassification.");
                // 添加诊断日志，看看 "全部房间" 下到底有哪些子节点
                if (CategoryTreeNodes != null && CategoryTreeNodes.Count == 1 && CategoryTreeNodes[0].Children != null)
                {
                    Debug.WriteLine($"Children found under '{CategoryTreeNodes[0].DisplayName}': {string.Join(", ", CategoryTreeNodes[0].Children.Select(c => $"'{c.DisplayName}'"))}");
                }
                else
                {
                    Debug.WriteLine("Could not access children of the root node for diagnostics.");
                }
                // 显示错误消息给用户
                RevitContextHelper.Execute(app => TaskDialog.Show("错误", $"无法找到用于重新归类的“{defaultCategoryNodeName}”节点。"));
                return; // 找不到就退出
            }

            // 5. 防止删除 "其他类" 本身
            if (nodeToDelete == otherCategoryNode)
            {
                RevitContextHelper.Execute(app => TaskDialog.Show("提示", $"不能删除基础分类“{defaultCategoryNodeName}”。"));
                return;
            }

            // 6. 确认删除对话框
            var result = MessageBox.Show(m_MainView, $"确定要删除分类“{nodeToDelete.DisplayName}”吗？\n\n其下所有房间将被移到“{defaultCategoryNodeName}”。\n此操作不可撤销！", "确认删除", MessageBoxButton.YesNo, MessageBoxImage.Warning);

            if (result == MessageBoxResult.Yes)
            {
                Debug.WriteLine($"准备删除分类 '{nodeToDelete.DisplayName}'...");

                // 7. 获取需要重新分配的房间
                var roomsToReassign = nodeToDelete.GetAllRoomsInChildren();
                Debug.WriteLine($"找到 {roomsToReassign.Count} 个房间需要重新分配到 '{defaultCategoryNodeName}'。");

                // 8.   执行 Revit 操作：修改房间参数    
                if (roomsToReassign.Any()) // 只有当有房间需要移动时才执行
                {
                    List<ElementId> roomIdsToReassign = roomsToReassign.Select(r => r.Id).ToList();
                    RevitContextHelper.Execute(uiApp =>
                    {
                        Document localDoc = uiApp.ActiveUIDocument.Document;
                        ExternalDefinition sharedParamDef = SharedParameterHelper.EnsureParameterExistsAndBound(uiApp.Application, localDoc);
                        if (sharedParamDef == null) return; // 确保参数存在

                        using (Transaction tx = new Transaction(localDoc, $"移动房间到 {defaultCategoryNodeName}"))
                        {
                            try
                            {
                                tx.Start();
                                int successCount = 0;
                                foreach (ElementId roomId in roomIdsToReassign)
                                {
                                    Room room = localDoc.GetElement(roomId) as Room;
                                    if (room != null)
                                    {
                                        Parameter param = room.get_Parameter(sharedParamDef);
                                        if (param != null && !param.IsReadOnly)
                                        {
                                            if (param.Set(defaultCategoryNodeName)) // 设置为 "其他类"
                                            {
                                                successCount++;
                                            }
                                        }
                                    }
                                }
                                if (successCount > 0) tx.Commit(); else tx.RollBack();
                                Debug.WriteLine($"Revit Parameter Update: Updated {successCount} rooms to '{defaultCategoryNodeName}'.");
                            }
                            catch (Exception ex)
                            {
                                Debug.WriteLine($"Revit Transaction ERROR during room reassignment: {ex.Message}");
                                if (tx.HasStarted() && tx.GetStatus() == TransactionStatus.Started) tx.RollBack();
                            }
                        }
                    });
                }

                // 9. 更新 ViewModel 树结构和关联
                // 将房间从旧节点移动到 "其他类" 节点 (ViewModel层面)
                otherCategoryNode.AssociatedRooms.AddRange(roomsToReassign);
                // (可选，如果 GetAllRoomsInChildren 不包含父节点自己) nodeToDelete.AssociatedRooms.Clear();

                // 从父节点的 Children 集合中移除 nodeToDelete
                if (nodeToDelete.Parent != null) // Parent 应该是 allRoomsNode
                {
                    if (nodeToDelete.Parent.Children.Remove(nodeToDelete))
                    {
                        Debug.WriteLine($"分类 '{nodeToDelete.DisplayName}' 已从父节点 '{nodeToDelete.Parent.DisplayName}' 的子集合中移除。");
                    }
                    else
                    {
                        Debug.WriteLine($"WARNING: Failed to remove '{nodeToDelete.DisplayName}' from parent '{nodeToDelete.Parent.DisplayName}'.");
                    }
                }
                else
                {
                    Debug.WriteLine($"WARNING: Node to delete '{nodeToDelete.DisplayName}' has no Parent.");
                    // 如果是根用户节点，它的 Parent 就是 allRoomsNode
                    CategoryTreeNodes[0]?.Children?.Remove(nodeToDelete); // 尝试直接从根的子节点移除
                }

                // 10. 保存配置
                SaveCategoryConfiguration();

                // 11. 刷新界面
                UpdateRoomList(); // 更新右侧房间列表 (可能需要选中 otherCategoryNode?)
                UpdateAllNodeCounts(CategoryTreeNodes); // 更新树节点的计数

                // (可选) 选中 "其他类" 节点
                // SelectedCategoryNode = otherCategoryNode;
            }
            else
            {
                Debug.WriteLine("删除操作已取消。");
            }
        } // 方法结束

        /// <summary>
        /// 判断是否可以执行修改（重命名/删除）分类的命令。
        /// </summary>
        private bool CanExecuteModifyCategory(CategoryTreeNodeViewModel node)
        {
            return node != null && !node.DisplayName.Equals("其他类", StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// 执行房间归类操作 (由拖放或未来可能的其他方式调用)。
        /// </summary>
        private void ExecuteAssignRoomsToSpecificCategory(AssignCategoryParameter parameter)
        {
            // 1. 参数验证和确认弹框 (这些可以在 UI 线程执行)
            if (parameter?.TargetCategoryNode == null || parameter.SelectedRoomVMs == null || !parameter.SelectedRoomVMs.Any())
            { Debug.WriteLine("归类命令参数无效。"); TaskDialog.Show("提示", "归类操作所需参数不足。"); return; }
            if (parameter.TargetCategoryNode.IsAllRoomsNode || parameter.TargetCategoryNode.DisplayName.Equals("其他类", StringComparison.OrdinalIgnoreCase))
            { TaskDialog.Show("提示", "不能将房间归类到“全部房间”或“其他类”。"); return; }

            CategoryTreeNodeViewModel targetNode = parameter.TargetCategoryNode;
            List<RoomViewModel> selectedVMs = parameter.SelectedRoomVMs;
            int roomCount = selectedVMs.Count;
            string targetCategoryName = targetNode.DisplayName; // 获取目标分类名称

            // 提取需要传递给 Revit 上下文的房间 ID 列表
            // 注意：直接传递 Room 对象或 RoomViewModel 对象到 ExternalEvent 可能不稳定或失败
            // 最好只传递 ElementId 或其他简单数据类型
            List<ElementId> roomIdsToAssign = selectedVMs.Select(vm => vm.RevitRoom?.Id).Where(id => id != null && id != ElementId.InvalidElementId).ToList();
            if (!roomIdsToAssign.Any())
            {
                TaskDialog.Show("提示", "没有有效的房间可供归类。");
                return;
            }


            var confirmResult = MessageBox.Show(m_MainView, $"确定要将选中的 {roomCount} 个房间归类到“{targetCategoryName}”吗？", "确认归类", MessageBoxButton.YesNo, MessageBoxImage.Question);
            if (confirmResult != MessageBoxResult.Yes) { Debug.WriteLine("用户取消了归类操作。"); return; }


            // 2. 使用 RevitContextHelper.Execute 来执行需要 Revit API 上下文的操作
            RevitContextHelper.Execute(uiApp => // uiApp 参数由 RevitContextHelper 提供
            {
                Document localDoc = uiApp.ActiveUIDocument.Document; // 在事件处理器内获取当前文档

                // 确保在事件处理器内部获取或验证共享参数定义
                // 注意：EnsureParameterExistsAndBound 内部也可能启动事务，所以它必须在 Execute 内部调用
                ExternalDefinition sharedParamDef = SharedParameterHelper.EnsureParameterExistsAndBound(uiApp.Application, localDoc);
                if (sharedParamDef == null)
                {
                    // SharedParameterHelper 内部应已显示 TaskDialog
                    return; // 无法继续
                }

                // 在这里启动事务
                using (Transaction tx = new Transaction(localDoc, $"归类房间到 {targetCategoryName}"))
                {
                    try
                    {
                        tx.Start();
                        int sCount = 0;
                        int fCount = 0;

                        foreach (ElementId roomId in roomIdsToAssign)
                        {
                            Room room = localDoc.GetElement(roomId) as Room;
                            if (room == null || !room.IsValidObject)
                            {
                                Debug.WriteLine($"在 Revit 上下文中找不到或无效的房间 ID: {roomId}");
                                fCount++;
                                continue;
                            }

                            Parameter param = room.get_Parameter(sharedParamDef);
                            if (param != null && !param.IsReadOnly)
                            {
                                // 设置参数值
                                if (param.Set(targetCategoryName))
                                {
                                    sCount++;
                                }
                                else
                                {
                                    Debug.WriteLine($"未能设置房间 {room.Number} 的参数 '{SharedParameterHelper.SharedParameterName}'。");
                                    fCount++;
                                }
                            }
                            else
                            {
                                Debug.WriteLine($"房间 {room.Number} 未找到参数 '{SharedParameterHelper.SharedParameterName}' 或参数只读。");
                                fCount++;
                            }
                        }

                        if (sCount > 0)
                        {
                            tx.Commit();
                            // 成功提示现在也应在 Revit 上下文中显示
                            TaskDialog.Show("完成", $"成功归类 {sCount} 个房间。\n失败/跳过 {fCount} 个。");

                            // **** UI 更新需要回到 UI 线程 ****
                            // 不要在这里直接调用 m_MainView 或 LoadDataCommand
                            // 可以考虑设置一个标志位，或者通过其他机制通知 ViewModel 需要刷新
                            // 例如，ViewModel 可以监听 ExternalEvent 完成的某种信号
                        }
                        else
                        {
                            tx.RollBack();
                            TaskDialog.Show("提示", $"没有房间被成功归类。\n失败/跳过 {fCount} 个。");
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine($"归类事务失败: {ex.Message}\n{ex.StackTrace}");
                        if (tx.HasStarted() && !tx.HasEnded()) tx.RollBack();
                        TaskDialog.Show("错误", $"归类时出错: {ex.Message}");
                    }
                } // using Transaction 结束
            }); // RevitContextHelper.Execute 结束


            // 3. 请求 UI 刷新 (需要回到 UI 线程)
            m_MainView.Dispatcher.InvokeAsync(async () =>
            {
                // 稍微增加延迟，但仍不能完全保证时序
                await Task.Delay(200); // 增加到 200ms 试试
                Debug.WriteLine("尝试在拖放操作后执行部分刷新...");

                // --- 调用部分刷新方法 ---
                RefreshRoomAssociationsAndUI();
                // ------------------------
            });
        }

        /// <summary>
        /// 判断归类命令是否可执行。
        /// </summary>
        private bool CanExecuteAssignRoomsToSpecificCategory(AssignCategoryParameter parameter)
        {
            // 检查参数、目标节点是否为特殊节点（全部/其他类）并且是否为叶节点
            return parameter?.TargetCategoryNode != null
                   && !parameter.TargetCategoryNode.IsAllRoomsNode                     // 不能是 "全部房间"
                   && !parameter.TargetCategoryNode.DisplayName.Equals("其他类", StringComparison.OrdinalIgnoreCase) // 不能是 "其他类"
                   && parameter.TargetCategoryNode.IsLeafNode                         // <<< --- 新增：必须是叶节点 --- <<<
                   && parameter.SelectedRoomVMs != null
                   && parameter.SelectedRoomVMs.Any();
        }

        /// <summary>
        /// 执行确保共享参数存在的逻辑。
        /// </summary>
        // (在 SpaceManagerVm.cs 中)
        private void ExecuteEnsureSharedParameter()
        {
            Debug.WriteLine("开始执行确保共享参数操作 (通过 RevitContextHelper)...");
            RevitContextHelper.Execute(uiApp => // 使用 RevitContextHelper
            {
                try
                {
                    // 在事件处理器内获取 Application 和 Document
                    Application localApp = uiApp.Application;
                    Document localDoc = uiApp.ActiveUIDocument.Document;

                    // 调用辅助类方法来确保参数存在并已绑定
                    ExternalDefinition definition = SharedParameterHelper.EnsureParameterExistsAndBound(localApp, localDoc);

                    if (definition != null)
                    {
                        // 成功信息（可选，也可以在 Helper 内部处理）
                        Debug.WriteLine($"共享参数 '{SharedParameterHelper.SharedParameterName}' 已成功确保存在并绑定。");
                        // TaskDialog.Show("成功", "共享参数已成功配置。");
                    }
                    else
                    {
                        // Helper 内部应已显示错误信息
                        Debug.WriteLine($"未能确保共享参数 '{SharedParameterHelper.SharedParameterName}'。请检查之前的错误消息或 Revit 日志。");
                    }
                }
                catch (Exception ex)
                {
                    // 捕获在 Revit 上下文中发生的意外错误
                    Debug.WriteLine($"执行 ExecuteEnsureSharedParameter 时在 Revit 上下文发生意外错误: {ex.ToString()}");
                    TaskDialog.Show("严重错误", $"检查或创建共享参数时发生意外错误: {ex.Message}");
                }
            });
            Debug.WriteLine("确保共享参数操作请求已发送。");
        }

        // --- 核心辅助方法 ---


        /// <summary>
        /// 递归地展开指定节点的所有父节点，使其在 TreeView 中可见。
        /// </summary>
        /// <param name="node">需要确保其祖先节点都展开的子节点。</param>
        private void ExpandParents(CategoryTreeNodeViewModel node)
        {
            // 检查当前节点及其父节点是否有效
            if (node?.Parent != null)
            {
                // 如果父节点当前未展开，则将其展开
                if (!node.Parent.IsExpanded)
                {
                    node.Parent.IsExpanded = true;
                    // Debug.WriteLine($"Expanded parent: {node.Parent.DisplayName}"); // 可选的调试日志
                }
                // 继续向上递归处理父节点的父节点
                ExpandParents(node.Parent);
            }
            // 如果节点为 null 或没有父节点 (即根节点)，递归停止
        }

        /// <summary>
        /// 递归计算并更新指定节点及其子孙节点的 TotalRoomCount 属性。
        /// </summary>
        /// <param name="node">要计算的节点</param>
        /// <returns>该节点及其子孙关联的房间总数</returns>
        private int UpdateNodeCountsRecursively(CategoryTreeNodeViewModel node)
        {
            if (node == null) return 0;

            // 递归计算所有子节点的总数
            int childrenCount = 0;
            if (node.Children != null) // 添加 null 检查
            {
                foreach (var child in node.Children)
                {
                    childrenCount += UpdateNodeCountsRecursively(child);
                }
            }

            // 当前节点的总数 = 直接关联的房间数 + 所有子节点的总数
            // 特殊处理 "全部房间" 节点，其总数就是直接关联的房间数 (已按楼层筛选)
            int totalCount = node.IsAllRoomsNode
                             ? node.AssociatedRooms.Count
                             : node.AssociatedRooms.Count + childrenCount;


            // 更新节点上的属性 (会触发 UI 更新，如果 XAML 绑定了的话)
            node.TotalRoomCount = totalCount;

            // 返回当前节点的总数，供父节点使用
            return totalCount;
        }

        /// <summary>
        /// 遍历并更新整个分类树所有节点的房间总数。
        /// </summary>
        /// <param name="rootNodes">要更新的树的根节点集合</param>
        private void UpdateAllNodeCounts(ObservableCollection<CategoryTreeNodeViewModel> rootNodes)
        {
            if (rootNodes == null) return;
            Debug.WriteLine("--- 开始更新所有节点计数 ---");
            foreach (var rootNode in rootNodes)
            {
                // 从每个根节点开始递归更新
                UpdateNodeCountsRecursively(rootNode);
            }
            Debug.WriteLine("--- 所有节点计数更新完毕 ---");
        }

        /// <summary>
        /// 应用当前的楼层筛选，加载配置，构建分类树，并更新房间列表。
        /// </summary>
        private void ApplyFiltersAndUpdateView()
        {
            Debug.WriteLine($"应用筛选。当前选中楼层: {GetSelectedLevelName()}");
            List<Room> roomsToProcess;
            Level currentSelectedLevel = SelectedLevel as Level;

            if (SelectedLevel == AllLevelsPlaceholder || currentSelectedLevel == null)
            {
                roomsToProcess = _allRooms;
                Debug.WriteLine($"楼层筛选：全部楼层，使用 {_allRooms.Count} 个房间。");
            }
            else
            {
                const double elevationTolerance = 0.001;
                // *** 确保这里的 Where 子句完整 ***
                roomsToProcess = _allRooms.Where(r => {
                    Level roomLevel = r.Document.GetElement(r.LevelId) as Level;
                    return roomLevel != null && Math.Abs(roomLevel.Elevation - currentSelectedLevel.Elevation) < elevationTolerance;
                }).ToList();
                // ********************************
                Debug.WriteLine($"楼层筛选：{currentSelectedLevel.Name}，筛选后得到 {roomsToProcess.Count} 个房间。");
            }
            var categoryConfig = LoadCategoryConfiguration();
            BuildCategoryTree(categoryConfig, roomsToProcess);
            UpdateRoomList();
        }


        /// <summary>
        /// 根据当前选中的分类树节点、楼层筛选和文本筛选，更新右侧 DataGrid 中的房间列表。
        /// </summary>
        private void UpdateRoomList()
        {
            Debug.WriteLine($"更新房间列表。分类: {SelectedCategoryNode?.DisplayName ?? "无"}, 楼层: {GetSelectedLevelName()}, 筛选文本: '{RoomFilterText}'");
            var newFilteredList = new ObservableCollection<RoomViewModel>();
            List<Room> finalRooms = new List<Room>(); // 先准备空列表

            if (SelectedCategoryNode != null)
            {
                List<Room> roomsToShow;
                if (SelectedCategoryNode.IsAllRoomsNode) { roomsToShow = SelectedCategoryNode.AssociatedRooms; } else { roomsToShow = SelectedCategoryNode.GetAllRoomsInChildren(); }

                // 应用楼层筛选
                Level currentSelectedLevel = SelectedLevel as Level;
                List<Room> roomsAfterLevelFilter;
                if (SelectedLevel == AllLevelsPlaceholder || currentSelectedLevel == null) { roomsAfterLevelFilter = roomsToShow; }
                else { const double elevationTolerance = 0.001; roomsAfterLevelFilter = roomsToShow.Where(r => { Level roomLevel = r.Document.GetElement(r.LevelId) as Level; return roomLevel != null && Math.Abs(roomLevel.Elevation - currentSelectedLevel.Elevation) < elevationTolerance; }).ToList(); }

                // *** 应用名称/编号文本筛选 ***
                string filterText = RoomFilterText?.Trim(); // 获取筛选文本
                if (string.IsNullOrWhiteSpace(filterText))
                {
                    finalRooms = roomsAfterLevelFilter; // 无需文本筛选
                }
                else
                {
                    finalRooms = roomsAfterLevelFilter
                        .Where(r => r.Name != null && r.Name.IndexOf(filterText, StringComparison.OrdinalIgnoreCase) >= 0 || // 名称匹配 (忽略大小写)
                                    r.Number != null && r.Number.IndexOf(filterText, StringComparison.OrdinalIgnoreCase) >= 0) // 编号匹配 (忽略大小写)
                        .ToList();
                }
                // ***************************
            }
            else { Debug.WriteLine("未选中分类节点，房间列表为空。"); }

            // 创建 ViewModel
            Debug.WriteLine($"准备为 {finalRooms.Count} 个房间创建 ViewModel。");
            foreach (var room in finalRooms.OrderBy(r => r.Number)) { try { newFilteredList.Add(new RoomViewModel(room)); } catch (Exception ex) { Debug.WriteLine($"为房间 {room.Id} 创建 RoomViewModel 时出错: {ex.Message}"); } }

            FilteredRoomList = newFilteredList; // 触发 Setter 和 Count 更新
        }

        /// <summary>
        /// 从配置文件加载分类树结构。
        /// </summary>
        /// <returns>配置节点列表。如果文件不存在或出错，则返回默认结构。</returns>
        private List<CategoryNodeConfig> LoadCategoryConfiguration()
        {
            Debug.WriteLine($"从以下路径加载分类配置: {_configFilePath}");
            try
            {
                if (File.Exists(_configFilePath))
                {
                    string json = File.ReadAllText(_configFilePath);
                    var config = JsonConvert.DeserializeObject<List<CategoryNodeConfig>>(json);
                    Debug.WriteLine($"配置加载成功。根节点数: {config?.Count ?? 0}");
                    return config ?? new List<CategoryNodeConfig>(); // 防 null
                }
                else
                {
                    Debug.WriteLine("配置文件不存在。返回默认分类结构。");
                    return GetDefaultCategoryConfiguration();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"加载配置错误: {ex.Message}");
                TaskDialog.Show("配置错误", $"无法加载分类配置文件: {ex.Message}\n将使用默认结构。");
                return GetDefaultCategoryConfiguration(); // 出错时返回默认
            }
        }

        /// <summary>
        /// 将当前的 ViewModel 分类树结构保存到配置文件。
        /// </summary>
        private void SaveCategoryConfiguration()
        {
            Debug.WriteLine($"准备保存分类配置到: {_configFilePath}");
            try
            {
                // 1. 检查输入给转换函数的内容
                Debug.WriteLine($"CategoryTreeNodes count before conversion: {CategoryTreeNodes?.Count ?? -1}");
                // 你可以在这里添加更详细的日志，打印出 CategoryTreeNodes 中每个根节点的 DisplayName 和 IsAllRoomsNode 状态

                var configToSave = ConvertViewModelTreeToConfigList(CategoryTreeNodes);

                // 2. 检查转换函数的输出 configToSave
                if (configToSave == null)
                {
                    Debug.WriteLine("!!!!!!!!!! configToSave is NULL after ConvertViewModelTreeToConfigList !!!!!!!!!!");
                    // 如果 configToSave 是 null，这是第一个需要解决的问题！
                    TaskDialog.Show("转换错误", "转换 ViewModel 树时返回了 null！");
                    return; // 提前退出，避免后续错误
                }
                else
                {
                    Debug.WriteLine($"configToSave count after conversion: {configToSave.Count}");
                    // 你可以检查 configToSave.Count 是否符合预期（例如，是否为 0）
                }

                // 3. 执行序列化
                string json = JsonConvert.SerializeObject(configToSave, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });

                // 4.   关键检查：检查序列化后的 json 变量    
                if (json == null)
                {
                    // 如果这里 json 确实是 C# null，问题非常奇怪，可能与 Newtonsoft.Json 库或环境有关
                    Debug.WriteLine("!!!!!!!!!! json variable is C# NULL after SerializeObject !!!!!!!!!!");
                    TaskDialog.Show("序列化错误", "JsonConvert.SerializeObject 返回了 C# null！");
                    // 可以考虑给 json 一个默认值，防止 File.WriteAllText 出错
                    json = "[]"; // 例如写入一个空数组，而不是尝试写入 null
                }
                else
                {
                    // 输出实际的 json 值，看看到底是什么
                    Debug.WriteLine($"Serialized json string: '{json}'"); // 注意单引号，方便看清是否为空或 "null"
                }

                // 5. 写入文件 (如果 json 不是 C# null)
                File.WriteAllText(_configFilePath, json); // 如果上一步给了默认值，这里不会出错
                Debug.WriteLine("配置保存成功。");
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"保存配置错误: {ex.Message}\n{ex.StackTrace}"); // 添加 StackTrace 以获取更详细信息
                TaskDialog.Show("保存错误", $"无法保存分类配置文件: {ex.Message}");
            }
        }

        /// <summary>
        /// 将 ViewModel 树转换为用于 JSON 存储的 Config 列表的主入口点。
        /// 处理顶层的特殊 "全部房间" 节点。
        /// </summary>
        private List<CategoryNodeConfig> ConvertViewModelTreeToConfigList(ObservableCollection<CategoryTreeNodeViewModel> vmNodes)
        {
            // vmNodes 在这里应该是 this.CategoryTreeNodes，它只包含 allRoomsNode
            if (vmNodes == null || !vmNodes.Any())
            {
                Debug.WriteLine("ConvertViewModelTreeToConfigList (Entry): Input vmNodes is null or empty.");
                return new List<CategoryNodeConfig>();
            }

            // 预期只有一个根节点，即 allRoomsNode
            if (vmNodes.Count == 1 && vmNodes[0].IsAllRoomsNode)
            {
                // 获取 allRoomsNode 的子节点，这些才是需要保存到 JSON 的实际根节点
                var actualRootsToSave = vmNodes[0].Children;
                Debug.WriteLine($"ConvertViewModelTreeToConfigList (Entry): Detected top-level 'All Rooms'. Processing its {actualRootsToSave?.Count ?? 0} children as JSON roots.");
                // 调用递归辅助函数来处理这些实际的根节点
                return ConvertVmNodesToConfigRecursive(actualRootsToSave);
            }
            else
            {
                // 如果入口处的 vmNodes 结构不符合预期（比如不只一个节点，或者那个节点不是 AllRoomsNode）
                // 这表示 ViewModel 状态可能不正确。记录错误并返回空列表以避免保存错误数据。
                Debug.WriteLine($"ERROR: ConvertViewModelTreeToConfigList (Entry): Expected a single 'All Rooms' root node in CategoryTreeNodes, but found {vmNodes.Count} nodes or the node was incorrect.");
                // 你可能还想打印出 vmNodes 的内容以供调试
                return new List<CategoryNodeConfig>();
            }
        }

        /// <summary>
        /// 递归地将 ViewModel 节点集合（用户定义的树部分）转换为 Config 节点列表。
        /// </summary>
        private List<CategoryNodeConfig> ConvertVmNodesToConfigRecursive(ObservableCollection<CategoryTreeNodeViewModel> vmNodes)
        {
            if (vmNodes == null || !vmNodes.Any())
            {
                return new List<CategoryNodeConfig>(); // 基本情况：没有节点或子节点
            }

            var configNodes = new List<CategoryNodeConfig>();

            foreach (var vmNode in vmNodes)
            {
                // 在递归中不应再遇到 IsAllRoomsNode == true 的情况，但加个保险
                if (vmNode.IsAllRoomsNode)
                {
                    Debug.WriteLine($"WARNING: ConvertVmNodesToConfigRecursive encountered node '{vmNode.DisplayName}' with IsAllRoomsNode=true. Skipping.");
                    continue;
                }

                // 为当前用户定义的节点创建 Config 对象
                var configNode = new CategoryNodeConfig { Name = vmNode.DisplayName };

                // 递归处理子节点
                if (vmNode.Children != null && vmNode.Children.Any())
                {
                    //   确保调用的是递归辅助函数    
                    configNode.Children = ConvertVmNodesToConfigRecursive(vmNode.Children);
                }
                else
                {
                    configNode.Children = new List<CategoryNodeConfig>();
                }
                configNodes.Add(configNode);
            }
            return configNodes;
        }

        /// <summary>
        /// 辅助方法：递归查找具有指定显示名称的第一个节点（忽略大小写）。
        /// </summary>
        private CategoryTreeNodeViewModel FindNodeByName(ObservableCollection<CategoryTreeNodeViewModel> nodes, string name)
        {
            if (nodes == null || string.IsNullOrWhiteSpace(name)) return null;

            foreach (var node in nodes)
            {
                // 移除或注释掉下面这行! 允许搜索 "全部房间" 的子节点
                // if (node.IsAllRoomsNode) continue; 

                // 检查当前节点名称是否匹配（忽略大小写）
                if (node.DisplayName.Equals(name, StringComparison.OrdinalIgnoreCase))
                {
                    // 找到了！但要确保我们找到的不是 "全部房间" 本身（如果搜索词恰好是 "全部房间"）
                    // 如果允许返回 "全部房间"，则可以直接 return node;
                    // 如果不允许，可以添加检查: if (!node.IsAllRoomsNode) return node;
                    // 对于查找 "其他类" 的场景，直接返回即可
                    return node;
                }

                // 如果当前节点不匹配，递归查找其子节点
                // 确保 Children 集合存在且有内容才递归
                if (node.Children != null && node.Children.Count > 0)
                {
                    var foundInChildren = FindNodeByName(node.Children, name);
                    if (foundInChildren != null)
                    {
                        return foundInChildren; // 在子节点中找到了！
                    }
                }
            }
            // 在当前层级和所有子层级（对于起始节点）都没找到
            return null;
        }
        /// <summary>
        /// 提供一个默认的分类树结构。
        /// </summary>
        private List<CategoryNodeConfig> GetDefaultCategoryConfiguration()
        {
            // 定义默认层级结构
            return new List<CategoryNodeConfig> {
                new CategoryNodeConfig { Name = "功能房间", Children = new List<CategoryNodeConfig> {
                    new CategoryNodeConfig { Name = "办公室" }, new CategoryNodeConfig { Name = "会议室" }, new CategoryNodeConfig { Name = "门厅" } } },
                new CategoryNodeConfig { Name = "性能房间", Children = new List<CategoryNodeConfig> {
                    new CategoryNodeConfig { Name = "核心筒", Children = new List<CategoryNodeConfig> {
                         new CategoryNodeConfig { Name = "走廊"}, new CategoryNodeConfig { Name = "卫生间"} } },
                    new CategoryNodeConfig { Name = "标准层机房" } } },
                new CategoryNodeConfig { Name = "其他类" } // 默认分类
            };
        }



        /// <summary>
        /// 根据加载的配置结构构建 ViewModel 树，并将房间关联到节点。
        /// </summary>
        /// <param name="configNodes">从 JSON 加载的配置节点列表。</param>
        /// <param name="rooms">已按楼层筛选过的房间列表。</param>
        private void BuildCategoryTree(List<CategoryNodeConfig> configNodes, List<Room> rooms)
        {
            // --- 初始化 ---
            Debug.WriteLine($"开始 BuildCategoryTree (修正版+日志)。传入配置根节点数: {configNodes?.Count ?? 0}，待处理房间数: {rooms?.Count ?? 0}。");
            var userDefinedRootNodes = new ObservableCollection<CategoryTreeNodeViewModel>();
            var vmNodeLookup = new Dictionary<string, CategoryTreeNodeViewModel>(StringComparer.OrdinalIgnoreCase);
            var safeRooms = rooms ?? new List<Room>();

            // --- 步骤 1: 从 JSON 配置递归构建用户定义的 ViewModel 树结构 ---
            BuildVmTreeRecursive(configNodes ?? new List<CategoryNodeConfig>(), null, userDefinedRootNodes, vmNodeLookup);
            Debug.WriteLine($"从配置构建了 {userDefinedRootNodes.Count} 个用户定义的根 ViewModel 节点。vmNodeLookup 包含 {vmNodeLookup.Count} 个用户定义条目。");

            // --- 步骤 2: 确定并确保用作默认分类的 "其他类" 节点存在 ---
            const string defaultCategoryNodeName = "其他类";
            CategoryTreeNodeViewModel defaultNode = FindNodeByName(userDefinedRootNodes, defaultCategoryNodeName);

            if (defaultNode == null)
            {
                Debug.WriteLine($"'{defaultCategoryNodeName}' 节点未在加载的用户配置树中找到。将创建默认的根节点...");
                defaultNode = new CategoryTreeNodeViewModel(defaultCategoryNodeName, null);
                userDefinedRootNodes.Add(defaultNode);
                string defaultPath = GetNodePath(defaultNode);
                if (!vmNodeLookup.ContainsKey(defaultPath))
                {
                    vmNodeLookup.Add(defaultPath, defaultNode);
                }
                Debug.WriteLine($"已创建并添加默认根节点 '{defaultCategoryNodeName}'。userDefinedRootNodes 现包含 {userDefinedRootNodes.Count} 个节点。");
            }
            else
            {
                Debug.WriteLine($"'{defaultCategoryNodeName}' 节点在加载的用户配置树中找到，路径为: '{GetNodePath(defaultNode)}'。将使用此节点作为默认分类。");
                defaultNode.AssociatedRooms.Clear();
            }

            if (defaultNode == null)
            {
                Debug.WriteLine($"    严重错误：获取或创建 '{defaultCategoryNodeName}' 节点后其引用仍然为 null！房间关联将失败。      ");
                TaskDialog.Show("致命配置错误", $"无法找到或创建必要的 '{defaultCategoryNodeName}' 分类节点。");
                CategoryTreeNodes = new ObservableCollection<CategoryTreeNodeViewModel>();
                return;
            }

            // --- 步骤 3: 显式创建动态的 "全部房间" 节点 ---
            const string allRoomsNodeName = "全部房间";
            var allRoomsNode = new CategoryTreeNodeViewModel(allRoomsNodeName, null)
            {
                IsAllRoomsNode = true,
                IsExpanded = true
            };
            allRoomsNode.AssociatedRooms.Clear();
            allRoomsNode.AssociatedRooms.AddRange(safeRooms);
            Debug.WriteLine($"创建了动态节点 '{allRoomsNodeName}'。IsAllRoomsNode={allRoomsNode.IsAllRoomsNode}。关联房间数: {allRoomsNode.AssociatedRooms.Count}");

            // --- 步骤 4: 将房间关联到具体的用户定义分类节点（或默认的 "其他类"）---
            const string categoryParamName = "空间类别";
            Debug.WriteLine($"--- 开始关联 {safeRooms.Count} 个房间到具体分类或默认分类 '{defaultNode.DisplayName}' ---");
            ClearAllRoomAssociations(userDefinedRootNodes); // 清空用户树的旧关联

            foreach (var room in safeRooms)
            {
                CategoryTreeNodeViewModel targetNode = defaultNode;
                string paramValueRead = null;
                string logPrefix = $"房间 ID {room.Id,-8} (编号: {room.Number ?? "N/A",-5}): ";

                try
                {
                    Parameter param = room.LookupParameter(categoryParamName);
                    if (param != null && param.HasValue)
                    {
                        paramValueRead = param.AsString();
                        if (!string.IsNullOrWhiteSpace(paramValueRead))
                        {
                            var foundNode = FindNodeByName(userDefinedRootNodes, paramValueRead);
                            if (foundNode != null)
                            {
                                targetNode = foundNode;
                            }
                            else
                            {
                                Debug.WriteLine(logPrefix + $"--X--> 参数值 '{paramValueRead}' 在用户定义树中未找到。将使用默认分类。");
                                targetNode = defaultNode;
                            }
                        }
                        else { targetNode = defaultNode; }
                    }
                    else { targetNode = defaultNode; }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(logPrefix + $"!!!!!! 读取参数 '{categoryParamName}' 时异常: {ex.Message} !!!!!! 将使用默认分类。");
                    targetNode = defaultNode;
                }

                if (targetNode != null)
                {
                    if (!targetNode.AssociatedRooms.Contains(room))
                    {
                        targetNode.AssociatedRooms.Add(room);
                    }
                }
                else
                {
                    Debug.WriteLine(logPrefix + $"!!!!!! 严重错误: 目标节点为 null！无法关联此房间。 !!!!!!");
                }
            }
            Debug.WriteLine($"--- 房间关联结束 ---");

            // --- 步骤 5: 将用户定义的根节点设置为动态 "全部房间" 节点的子节点 ---
            Debug.WriteLine($"--- Debug: 将 userDefinedRootNodes ({userDefinedRootNodes.Count} 个) 设置为 '{allRoomsNodeName}' 的子节点 ---");
            allRoomsNode.Children.Clear(); // 清空动态节点的子节点（以防万一）
            foreach (var userNode in userDefinedRootNodes)
            {
                // 再次检查，防止意外加载的 "全部房间" 成为子节点
                if (userNode.DisplayName.Equals(allRoomsNodeName, StringComparison.OrdinalIgnoreCase))
                {
                    Debug.WriteLine($"警告：跳过将名为 '{allRoomsNodeName}' 的 userNode 添加为子节点。");
                    continue;
                }
                allRoomsNode.Children.Add(userNode); // 将用户节点添加到动态节点的 Children 集合
                userNode.Parent = allRoomsNode;      //   关键: 更新子节点的 Parent 引用    
                Debug.WriteLine($"  - UserNode '{userNode.DisplayName}' 已添加为 '{allRoomsNodeName}' 的子节点。");
            }
            Debug.WriteLine($"动态节点 '{allRoomsNodeName}' 现在有 {allRoomsNode.Children.Count} 个子节点。");


            // --- 步骤 6: 构建最终的根节点集合，现在只包含动态的 "全部房间" 节点 ---
            var finalRootNodes = new ObservableCollection<CategoryTreeNodeViewModel>();
            finalRootNodes.Add(allRoomsNode); //   关键: 最终的根节点列表只包含 allRoomsNode    
            Debug.WriteLine($"构建最终根节点集合完成。总根节点数: {finalRootNodes.Count} (应只包含 '{allRoomsNodeName}')");

            // --- 步骤 7: 更新树中所有节点的房间计数 (从唯一的根节点 allRoomsNode 开始递归) ---
            UpdateAllNodeCounts(finalRootNodes);

            // --- 步骤 8: 将构建好的 ViewModel 树赋值给绑定的属性 ---
            CategoryTreeNodes = finalRootNodes;
            Debug.WriteLine($"ViewModel 树构建完成并分配给 CategoryTreeNodes。");
        } // 方法结束
        /// <summary>
        /// 递归辅助方法：根据配置节点列表构建 ViewModel 节点树。
        /// </summary>
        private void BuildVmTreeRecursive(List<CategoryNodeConfig> configNodes, CategoryTreeNodeViewModel parentVmNode, ObservableCollection<CategoryTreeNodeViewModel> vmNodeCollection, Dictionary<string, CategoryTreeNodeViewModel> vmNodeLookup)
        {
            if (configNodes == null) return;
            foreach (var configNode in configNodes) { if (string.IsNullOrWhiteSpace(configNode.Name)) continue; var newVmNode = new CategoryTreeNodeViewModel(configNode.Name, parentVmNode); string fullPath = GetNodePath(newVmNode); if (!vmNodeLookup.ContainsKey(fullPath)) { vmNodeLookup.Add(fullPath, newVmNode); } else { Debug.WriteLine($"警告：重复分类路径 '{fullPath}'。"); vmNodeLookup[fullPath] = newVmNode; } vmNodeCollection.Add(newVmNode); if (configNode.Children != null && configNode.Children.Any()) { BuildVmTreeRecursive(configNode.Children, newVmNode, newVmNode.Children, vmNodeLookup); } }
        }

        /// <summary>
        /// 辅助方法：获取指定 ViewModel 节点的完整路径。
        /// </summary>
        private string GetNodePath(CategoryTreeNodeViewModel node)
        {
            if (node == null) return string.Empty; var pathStack = new Stack<string>(); var currentNode = node; while (currentNode != null) { pathStack.Push(currentNode.DisplayName); currentNode = currentNode.Parent; }
            return string.Join("/", pathStack);
        }

        /// <summary>
        /// 辅助方法：递归清空所有节点的房间关联列表。
        /// </summary>
        private void ClearAllRoomAssociations(ObservableCollection<CategoryTreeNodeViewModel> nodes)
        {
            if (nodes == null) return;
            foreach (var node in nodes)
            {
                node.AssociatedRooms.Clear(); // 清空当前节点
                if (node.Children != null && node.Children.Any())
                {
                    ClearAllRoomAssociations(node.Children); // 递归清空子节点
                }
            }
        }

        /// <summary>
        /// 获取所有有效的房间（当前文档 + 链接文档）。
        /// </summary>
        private List<Room> GetAllRooms(Document currentDoc)
        {
            var rooms = new List<Room>();
            try { var currentProjectRooms = new FilteredElementCollector(currentDoc).OfCategory(BuiltInCategory.OST_Rooms).WhereElementIsNotElementType().Cast<Room>().Where(r => r != null && r.Area > 0 && r.Location != null).ToList(); rooms.AddRange(currentProjectRooms); Debug.WriteLine($"当前文档找到 {currentProjectRooms.Count} 个房间。"); } catch (Exception ex) { Debug.WriteLine($"获取当前文档房间出错: {ex.Message}"); }
            try { var linkInstances = new FilteredElementCollector(currentDoc).OfClass(typeof(RevitLinkInstance)).Cast<RevitLinkInstance>().ToList(); Debug.WriteLine($"找到 {linkInstances.Count} 个链接实例。"); foreach (var linkInstance in linkInstances) { Document linkDoc = null; try { linkDoc = linkInstance.GetLinkDocument(); if (linkDoc == null) { Debug.WriteLine($"链接实例 {linkInstance.Name} 文档为空。"); continue; } Debug.WriteLine($"处理链接: {linkDoc.Title}"); var linkRooms = new FilteredElementCollector(linkDoc).OfCategory(BuiltInCategory.OST_Rooms).WhereElementIsNotElementType().Cast<Room>().Where(r => r != null && r.Area > 0 && r.Location != null).ToList(); rooms.AddRange(linkRooms); Debug.WriteLine($"链接 {linkDoc.Title} 中找到 {linkRooms.Count} 个房间。"); } catch (Exception ex) { Debug.WriteLine($"处理链接 {linkInstance?.Name ?? "未知"} 时出错: {ex.Message}"); continue; } } } catch (Exception ex) { Debug.WriteLine($"获取链接实例出错: {ex.Message}"); }
            return rooms; // 确保返回列表
        }


        /// <summary>
        /// 执行部分刷新：根据 _allRooms 重新关联房间到当前树节点，并更新UI列表。
        /// </summary>
        private void RefreshRoomAssociationsAndUI()
        {
            Debug.WriteLine("执行部分刷新：重新关联房间并更新列表...");
            // 检查基本条件是否满足
            if (CategoryTreeNodes == null || !CategoryTreeNodes.Any() || _allRooms == null)
            {
                Debug.WriteLine("部分刷新跳过：分类树或房间总列表为空。");
                return;
            }

            // 1. 清空当前树中所有节点的现有房间关联
            ClearAllRoomAssociations(CategoryTreeNodes);

            // 2. 根据当前的楼层筛选器确定要处理的房间子集
            List<Room> roomsToProcess;
            Level currentSelectedLevel = SelectedLevel as Level;
            if (SelectedLevel == AllLevelsPlaceholder || currentSelectedLevel == null)
            {
                roomsToProcess = _allRooms; // 处理所有房间
            }
            else
            {
                const double elevationTolerance = 0.001; // 定义标高比较容差
                                                         // 筛选出属于当前选中楼层的房间
                roomsToProcess = _allRooms.Where(r => {
                    // 尝试安全地获取房间所在楼层
                    Level roomLevel = null;
                    try
                    {
                        if (r.LevelId != null && r.LevelId != ElementId.InvalidElementId)
                        {
                            roomLevel = r.Document.GetElement(r.LevelId) as Level;
                        }
                    }
                    catch (Exception ex)
                    {
                        // 处理获取楼层时可能发生的异常（例如元素已被删除）
                        Debug.WriteLine($"获取房间 {r.Id} 的楼层时出错: {ex.Message}");
                    }
                    // 比较楼层标高
                    return roomLevel != null && Math.Abs(roomLevel.Elevation - currentSelectedLevel.Elevation) < elevationTolerance;
                }).ToList();
            }
            Debug.WriteLine($"部分刷新：根据楼层筛选后，待处理房间数: {roomsToProcess.Count}");


            // 2.1 重新填充 "全部房间" 节点的关联列表 (很重要!)
            var allRoomsNode = CategoryTreeNodes.FirstOrDefault(n => n.IsAllRoomsNode);
            if (allRoomsNode != null)
            {
                // allRoomsNode.AssociatedRooms.Clear(); // 已在 ClearAllRoomAssociations 中完成
                allRoomsNode.AssociatedRooms.AddRange(roomsToProcess); // 添加当前楼层筛选后的所有房间
                Debug.WriteLine($"部分刷新：已更新 '全部房间' 节点的关联房间数: {allRoomsNode.AssociatedRooms.Count}");
            }
            else
            {
                Debug.WriteLine("部分刷新警告：未找到 '全部房间' 节点。");
            }


            // 3. 重新关联 `roomsToProcess` 到对应的分类节点（使用当前 CategoryTreeNodes）
            const string categoryParamName = "空间类别";
            const string defaultCategoryNodeName = "其他类";
            var defaultNode = FindNodeByName(CategoryTreeNodes, defaultCategoryNodeName); // 在当前树中查找默认节点

            if (defaultNode == null)
            {
                Debug.WriteLine($"    部分刷新错误：无法在当前树中找到默认节点 '{defaultCategoryNodeName}'！      ");
            }

            Debug.WriteLine($"--- 开始重新关联 {roomsToProcess.Count} 个房间 (部分刷新) ---");
            foreach (var room in roomsToProcess)
            {
                CategoryTreeNodeViewModel targetNode = defaultNode; // 默认指向“其他类”
                string paramValueRead = null;
                string logPrefix = $"Room ID {room.Id,-8} (Partial Refresh): ";

                try
                {
                    Parameter param = room.LookupParameter(categoryParamName);
                    if (param != null && param.HasValue)
                    {
                        paramValueRead = param.AsString();
                        if (!string.IsNullOrWhiteSpace(paramValueRead))
                        {
                            // 在当前 this.CategoryTreeNodes 树中查找
                            var foundNode = FindNodeByName(CategoryTreeNodes, paramValueRead);
                            if (foundNode != null)
                            {
                                targetNode = foundNode;
                            }
                            else
                            {
                                // Debug.WriteLine(logPrefix + $"Node NOT FOUND for '{paramValueRead}'. Using default.");
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(logPrefix + $"EXCEPTION reading param: {ex.Message}. Using default.");
                    targetNode = defaultNode;
                }

                // 添加关联
                if (targetNode != null)
                {
                    // 检查是否已存在（理论上因为先 Clear 了，不会重复，但加个保险）
                    if (!targetNode.AssociatedRooms.Contains(room))
                    {
                        targetNode.AssociatedRooms.Add(room);
                    }
                    // if (targetNode != defaultNode) Debug.WriteLine(logPrefix + $"Re-associated with '{targetNode.DisplayName}'");
                }
                else
                {
                    Debug.WriteLine(logPrefix + $"!!!!!! 严重错误: 无法重新关联房间，目标节点为 null !!!!!!");
                }
            }
            Debug.WriteLine($"--- 重新关联结束 (部分刷新) ---");

            // --- 在这里调用计数更新 ---
            UpdateAllNodeCounts(CategoryTreeNodes);
            // 更新当前树的计数


            // 4. 调用 UpdateRoomList 来刷新 DataGrid
            // UpdateRoomList 会读取刚更新过的 AssociatedRooms 列表
            UpdateRoomList();
            Debug.WriteLine("部分刷新完成。");
        }


        /// <summary>
        /// 辅助方法：根据完整路径在给定的 ViewModel 节点集合中查找节点。
        /// </summary>
        private CategoryTreeNodeViewModel FindNodeByPath(ObservableCollection<CategoryTreeNodeViewModel> nodes, string path)
        {
            if (nodes == null || path == null) return null; var pathSegments = path.Split('/'); CategoryTreeNodeViewModel currentNode = null; ObservableCollection<CategoryTreeNodeViewModel> currentLevelNodes = nodes;
            foreach (var segment in pathSegments) { if (currentLevelNodes == null) return null; currentNode = currentLevelNodes.FirstOrDefault(n => n.DisplayName.Equals(segment, StringComparison.OrdinalIgnoreCase)); if (currentNode == null) return null; currentLevelNodes = currentNode.Children; }
            return currentNode;
        }

        // --- 获取当前选中楼层的显示名称 ---
        private string GetSelectedLevelName()
        {
            if (SelectedLevel == AllLevelsPlaceholder) return "(全部楼层)";
            if (SelectedLevel is Level level) return level.Name;
            return "未知";
        }

    } // 结束 SpaceManagerVm 类
} // 结束命名空间